package com.atguigu.tingshu.common.aspect;

import com.alibaba.fastjson.JSONObject;
import com.atguigu.tingshu.common.constant.SystemConstant;
import com.atguigu.tingshu.common.execption.GuiguException;
import com.atguigu.tingshu.common.result.ResultCodeEnum;
import com.atguigu.tingshu.common.util.AuthContextHolder;
import jakarta.annotation.Resource;
import jakarta.servlet.http.HttpServletRequest;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.security.jwt.Jwt;
import org.springframework.security.jwt.JwtHelper;
import org.springframework.security.jwt.crypto.sign.RsaVerifier;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import java.util.Map;
import java.util.concurrent.TimeUnit;

/**
 * 登录权限切面
 *
 * @author Haoran Sun
 * @since 2024/08/16
 */

@Aspect
@Component
@Slf4j
public class LoginRequiredAspect {

    @Resource
    private RedisTemplate<String,String> redisTemplate;

    /**
     * 登录权限切点
     */
    @Pointcut("@annotation(com.atguigu.tingshu.common.annotation.login.LoginRequired)")
    public void loginRequiredPointCut() {
    }

    /**
     * 登录权限切面
     * @param pjp 程序执行点
     * @return 被切入方法的返回结果
     */
    @SneakyThrows
    @Around("loginRequiredPointCut()")
    public Object loginRequiredAspect(ProceedingJoinPoint pjp) {

        log.info("loginRequiredAspect-----------------------------------------");


        ServletRequestAttributes servletRequestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();

        if (servletRequestAttributes == null) {
            log.error("request is null");
            throw new GuiguException(ResultCodeEnum.FAIL);
        }
        HttpServletRequest request = servletRequestAttributes.getRequest();
        String token = request.getHeader("token");

        if (StringUtils.isEmpty(token)) {
            log.error("token is null");
            throw new GuiguException(ResultCodeEnum.LOGIN_AUTH);
        }

        //  验证token
        Jwt jwt = JwtHelper.decodeAndVerify(token, new RsaVerifier(SystemConstant.JWT_PUBLIC_KEY));
        //  解析token
        String claims = jwt.getClaims();
        Map map = JSONObject.parseObject(claims, Map.class);
        Long userId = Long.valueOf((String.valueOf(map.get(SystemConstant.JWT_CLEAMS_USER_ID))));
        // 验证是否过期
        String s = redisTemplate.opsForValue().get("user:login:token:" + userId);
        if (StringUtils.isEmpty(s)) {
            log.error("token过期");
            throw new GuiguException(ResultCodeEnum.LOGIN_AUTH);
        }
        if (!s.equals(token)) {
            log.error("使用了错误的token");
            throw new GuiguException(ResultCodeEnum.LOGIN_AUTH);
        }
        // token正确将token续期
        redisTemplate.expire("user:login:token:" + userId, 2 * 60, TimeUnit.MINUTES);
        //  在AuthContextHolder中设置用户id和用户名
        AuthContextHolder.setUserId(userId);
        AuthContextHolder.setUsername((String) map.get(SystemConstant.JWT_CLEAMS_USER_NAME));

        try {
            return pjp.proceed();
        } catch (Throwable e) {
            log.error("程序错误，{}",e.getMessage());
            throw new GuiguException(ResultCodeEnum.FAIL);
        } finally {
            AuthContextHolder.clear();
        }

    }
}
